package org.liziyi.pss.util;

import org.liziyi.pss.config.Config;
import org.liziyi.pss.entity.ProcessControlBlock;
import org.liziyi.pss.common.enums.ProcessAttributeEnum;
import org.liziyi.pss.common.enums.ProcessStatusEnum;

import javax.swing.table.TableModel;
import java.util.*;

/**
 * @author liziyi
 */
public class ProcessUtil {
    private static int pid;
    private static final Random random=new Random();

    public static int getNextPid() {
        return pid++;
    }

    public static void resetPid() {
        pid = 0;
    }
    public static void rollbackPid(int count){
        pid-=count;
    }

    public static Object[] pcb2TableRow(ProcessControlBlock pcb) {
        return new Object[]{pcb.getPid(), pcb.getTimeSlice(), pcb.getPriority(), pcb.getMemoryNeed(), pcb.getState(), pcb.getAttribute()};
    }
    public static ProcessControlBlock[] tableRow2Pcb(TableModel model){
        ProcessControlBlock[] pcbs=new ProcessControlBlock[model.getRowCount()];
        for(int i=0;i<model.getRowCount();i++){
            ProcessControlBlock pcb=new ProcessControlBlock();
            pcb.setPid((Integer) model.getValueAt(i,0));
            pcb.setTimeSlice((Integer) model.getValueAt(i,1));
            pcb.setTotalTimeSlice(pcb.getTimeSlice());
            pcb.setPriority((Integer) model.getValueAt(i,2));
            pcb.setMemoryNeed((Integer) model.getValueAt(i,3));
            pcb.setState((String)model.getValueAt(i,4));
            pcb.setAttribute((String)model.getValueAt(i,5));
            pcbs[i]=pcb;
        }
        return pcbs;
    }

    /**
     * 批量生成随机进程
     * @param num
     * @param withSync 是否带有同步进程
     * @return
     */
    public static ProcessControlBlock[] generateRandomProcess(int num,boolean withSync){
        ProcessControlBlock[] processControlBlocks=new ProcessControlBlock[num];
        // 随机生成num个独立进程
        for(int i=0;i<num;i++){
            ProcessControlBlock ps= new ProcessControlBlock();
            ps.setPid(ProcessUtil.getNextPid());
            ps.setTimeSlice(random.nextInt(Config.timeSliceNumMin,Config.timeSliceNumMax)+1);
            ps.setPriority(random.nextInt(Config.priorityMin,Config.priorityMax+1));
            ps.setMemoryNeed(random.nextInt(Config.memoryMin,Config.memoryMax+1));
            ps.setState(ProcessStatusEnum.NEW.toString());
            ps.setAttribute(ProcessAttributeEnum.NORMAL_PROCESS.getDesc());
            processControlBlocks[i]=ps;
        }
        // 如果需要带有同步进程，从这些独立进程中挑选出一些让它们具有同步关系
        if(withSync){
            // 同步进程总数
            int groupNum=(int)(num*Config.syncProcessScale);
            if(groupNum==0) return processControlBlocks;
            HashSet<Integer> set=new HashSet<>();
            List<Integer> list=Config.syncProcessGroupSize;
            String template=ProcessAttributeEnum.SYNC_PROCESS.getDesc();
            for(int i=0;i<groupNum;i++){
                // 一组同步进程中包含的进程数
                int groupSize=list.get(random.nextInt(list.size()));
                List<Integer> pcbList=new ArrayList<>(groupSize);
                // 选出groupSize个进程作为一组
                for(int j=0;j<groupSize;j++){
                    int tmp;
                    while(set.contains(tmp=random.nextInt(num)));
                    pcbList.add(tmp);
                    set.add(tmp);
                }
                // 这组同步进程需保证，前驱进程的优先级大于后继进程的优先级
                pcbList.sort(Comparator.comparingInt(a -> processControlBlocks[a].getPriority()));
                // 设置这组同步进程的attribute字段：同步进程(前驱进程pid/后继进程pid)
                ProcessControlBlock pcb=processControlBlocks[pcbList.get(0)];
                pcb.setAttribute(String.format(template,-1,processControlBlocks[pcbList.get(1)].getPid()));
                pcb=processControlBlocks[pcbList.get(groupSize-1)];
                pcb.setAttribute(String.format(template,processControlBlocks[pcbList.get(groupSize-2)].getPid(),-1));
                for(int j=1;j<groupSize-1;j++){
                    pcb=processControlBlocks[pcbList.get(j)];
                    pcb.setAttribute(String.format(template,processControlBlocks[pcbList.get(j-1)].getPid(),processControlBlocks[pcbList.get(j+1)].getPid()));
                }
            }
        }
        return processControlBlocks;
    }
}
